// Copyright 2008 web2me.dev
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.

package net.web2me.api.util;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import org.apache.log4j.Logger;

/**
 * An utility class to download conent
 * @author web2me
 */
public class Downloader implements Runnable {
	private static Logger logger = Logger.getLogger(Downloader.class);
	
	private String url;
	
	private URL theUrl;
	
	private URLConnection conn = null;
	
	private String destinyDirectory;
	
	private void stablishConnection() throws IOException{
		if(theUrl == null){
			theUrl = new URL(url);
			conn = theUrl.openConnection();
		}
	}
	
	/**
	 * Informs the file size to be download
	 * @return The file size. If it's not known returns -1.
	 */
	public int getFileSize(){
		try {
			stablishConnection();
			
			return conn.getContentLength();
		} catch (IOException e) {
			logger.error("An error occured getting the file size", e);
		}
		
		return -1;
	}
	
	/**
	 * Downloads the specified URL into the specified directory
	 * @param url A valid URL with the content to be downloaded
	 * @param destinyDirectory The directory where the content is going to be downloaded
	 * @throws UtilityException If an error ocurred
	 */
	public void download() throws UtilityException{
		
		if(url == null || destinyDirectory == null){
			throw new UtilityException("You must set the url and the destiny directory");
		}
		
		int begin = url.lastIndexOf("/");
		String fileName = "";
		
		if( destinyDirectory.substring(destinyDirectory.length() - 1).equals( "/" )){
			fileName = destinyDirectory + url.substring(begin + 1);
		}else{
			fileName = destinyDirectory + "/" + url.substring(begin + 1);
		}
		
		File directory = new File(destinyDirectory);
		
		if( !directory.exists() ){
			if(! directory.mkdirs() ){
				throw new UtilityException("The directory \"" + destinyDirectory + "\" couldn't be created");
			}
		}
		
		logger.info("Downloading " + url.toString());
		
		OutputStream out = null;
		
		InputStream  in = null;
		try {
			stablishConnection();
			
			out = new BufferedOutputStream(new FileOutputStream(fileName));
			
			in = conn.getInputStream();
			
			byte[] buffer = new byte[1024];
			int numRead;
			long numWritten = 0;
			
			while ((numRead = in.read(buffer)) != -1) {
				out.write(buffer, 0, numRead);
				numWritten += numRead;
			}
		} catch (MalformedURLException mue){
			logger.warn("MalformedURLException caught", mue);
		} catch (IOException ioe){
			logger.warn("IOException caught", ioe);
		} finally {
			try {
				if (in != null) {
					in.close();
				}
				
				if (out != null) {
					out.close();
				}
			} catch (IOException ioe) {
				logger.warn("IOException caught", ioe);
			}
		}
		
		File downloadedFile = new File(fileName);
		logger.debug( "The file " + fileName + " has " + downloadedFile.length() + " bytes" );
		
		if( downloadedFile.length() == 0 ){
			logger.warn( "The file \"" + fileName + "\" has falied. Retrying... " );	
			download();
		}else{
			logger.info("The file " + fileName + " has been downloaded");
		}
	}

	public void run() {
		try {
			download();
		} catch (UtilityException e) {
			throw new RuntimeException("UtilityException caught", e); 
		}
	}

	public String getUrl() {
		return url;
	}

	public void setUrl(String url) {
		this.url = url;
	}

	public String getDestinyDirectory() {
		return destinyDirectory;
	}

	public void setDestinyDirectory(String destinyDirectory) {
		this.destinyDirectory = destinyDirectory;
	}
}
